/*
 * This file is part of TradeHelper library.
 *
 * TradeHelper library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * TradeHelper library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with TradeHelper library.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.rag.assistants;

import com.alee.extended.label.WebLinkLabel;
import com.alee.extended.layout.WrapFlowLayout;
import com.alee.extended.panel.GroupPanel;
import com.alee.extended.window.TestFrame;
import com.alee.laf.WebLookAndFeel;
import com.alee.laf.button.WebButton;
import com.alee.laf.label.WebLabel;
import com.alee.laf.panel.WebPanel;
import com.alee.laf.progressbar.WebProgressBar;
import com.alee.laf.scroll.WebScrollPane;
import com.alee.utils.FileUtils;
import com.alee.utils.SwingUtils;
import com.alee.utils.XmlUtils;
import com.alee.utils.collection.ValuesTable;
import com.rag.core.Item;
import com.rag.core.ItemClass;
import com.rag.core.ItemType;
import com.rag.core.MobDrop;
import com.rag.managers.BaseManager;
import com.rag.managers.RagManager;
import com.rag.utils.RagUtils;
import info.clearthought.layout.TableLayout;
import net.htmlparser.jericho.Element;
import net.htmlparser.jericho.HTMLElementName;
import net.htmlparser.jericho.Segment;
import net.htmlparser.jericho.Source;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * User: mgarin Date: 04.04.12 Time: 18:51
 */

public class RmsDataLoader
{
    private static ImageIcon save =
            new ImageIcon ( RmsDataLoader.class.getResource ( "icons/save.png" ) );

    // 501-?
    //

    private static int startId = 27000;
    private static int endId = 35000;
    private static int currentId = startId;
    private static boolean shouldStop = false;

    private static List<Item> foundItemsList = new ArrayList<Item> ();
    private static Map<String, Item> foundItems = new HashMap<String, Item> ();

    private static ActionListener listener;

    private static long timeSpent;
    private static int count;

    public static void main ( String[] args )
    {
        WebLookAndFeel.install ();
        BaseManager.initialize ();
        RagManager.initialize ();

        new TestFrame ( new WebPanel ()
        {
            {
                setMargin ( 10 );
                TableLayout tableLayout = new TableLayout ( new double[][]{ { TableLayout.FILL },
                        { TableLayout.PREFERRED, TableLayout.PREFERRED, TableLayout.PREFERRED,
                                TableLayout.FILL } } );
                tableLayout.setHGap ( 8 );
                tableLayout.setVGap ( 8 );
                setLayout ( tableLayout );

                final WebLabel progressLabel = new WebLabel ( "Обрабатывается ID #" + currentId );
                progressLabel.setHorizontalAlignment ( WebLabel.CENTER );
                add ( progressLabel, "0,0" );

                final WebLabel timeLabel = new WebLabel ( "Вычисляем примерное время..." );
                timeLabel.setHorizontalAlignment ( WebLabel.CENTER );
                add ( timeLabel, "0,1" );

                final WebProgressBar progressBar = new WebProgressBar ();
                progressBar.setMinimum ( startId );
                progressBar.setMaximum ( endId );
                progressBar.setValue ( currentId );
                progressBar.setStringPainted ( true );
                progressBar.putClientProperty ( GroupPanel.FILL_CELL, true );

                WebButton stopAndSave = WebButton.createIconWebButton ( save );
                stopAndSave.setDrawFocus ( false );
                stopAndSave.addActionListener ( new ActionListener ()
                {
                    public void actionPerformed ( ActionEvent e )
                    {
                        stopParsing ();
                        saveResult ();
                    }
                } );

                add ( new GroupPanel ( 4, progressBar, stopAndSave ), "0,2" );

                final WebPanel found = new WebPanel ();
                found.setLayout ( new WrapFlowLayout ( false ) );
                found.setMargin ( 0, 0, 4, 4 );
                final WebScrollPane scrollPane = new WebScrollPane ( found )
                {
                    public Dimension getPreferredSize ()
                    {
                        Dimension ps = super.getPreferredSize ();
                        ps.height = 300;
                        return ps;
                    }
                };
                //                scrollPane.setPreferredSize ( new Dimension ( 1, 100 ) );
                scrollPane.setVerticalScrollBarPolicy ( WebScrollPane.VERTICAL_SCROLLBAR_ALWAYS );
                add ( scrollPane, "0,3" );

                // Слушатель апдейтов
                listener = new ActionListener ()
                {
                    private int lastIndex = -1;

                    public void actionPerformed ( ActionEvent e )
                    {
                        progressLabel.setText ( "Обрабатывается ID #" + currentId );
                        progressBar.setValue ( currentId );

                        long average = timeSpent / count;
                        long timeLeft = average * ( endId - currentId + 1 );
                        timeLabel.setText ( "Осталось примерно: " + getHumanTime ( timeLeft ) +
                                " (В среднем на ID: " + average + " мс)" );

                        if ( lastIndex != foundItemsList.size () - 1 )
                        {
                            Item item = foundItemsList.get ( foundItemsList.size () - 1 );
                            final WebLinkLabel link = new WebLinkLabel ();
                            link.setIcon ( item.getIcon () );
                            link.setText ( item.getText () );
                            link.setLink ( item.getRatemyserverLink (), false );
                            link.setMargin ( 4, 4, 0, 0 );
                            found.add ( link );

                            JScrollBar vsb = scrollPane.getVerticalScrollBar ();
                            if ( vsb.getValue () + vsb.getVisibleAmount () >= vsb.getMaximum () )
                            {
                                found.getLayout ().layoutContainer ( found );
                                Rectangle bounds = link.getBounds ();
                                bounds.height += 4;
                                found.scrollRectToVisible ( bounds );
                            }

                            lastIndex = foundItemsList.size () - 1;
                        }
                    }
                };
            }

            private void packFrame ()
            {
                SwingUtils.getWindowAncestor ( this ).pack ();
            }

            public Dimension getPreferredSize ()
            {
                Dimension ps = super.getPreferredSize ();
                ps.width = 600;
                return ps;
            }
        } );

        // Начинаем парсить элементы
        new Thread ( new Runnable ()
        {
            public void run ()
            {
                startParsing ();
            }
        } ).start ();
    }

    private static void stopParsing ()
    {
        shouldStop = true;
    }

    private static void startParsing ()
    {
        // Переменные для подсчёта времени исполнения
        timeSpent = 0;
        count = 0;

        // Проверяем весь спект ID
        int retryCount = 0;
        int id = currentId;
        while ( id <= endId )
        {
            long time = System.currentTimeMillis ();

            String page = loadInfoPage ( "" + id );
            if ( page != null )
            {
                if ( !page.contains ( "No match found." ) )
                {
                    System.out.println ( "Item #" + id + " found!" );

                    // Парсим HTML-содержимое страницы
                    Source source = new Source ( page );
                    source.fullSequentialParse ();

                    // Создаём объект под итем
                    Item item = new Item ( "" + id );

                    // Находим таблицу с описанием
                    for ( Element table : source.getAllElements ( HTMLElementName.TABLE ) )
                    {
                        String tableClass = table.getAttributeValue ( "class" );
                        if ( tableClass != null && tableClass.equals ( "content_box_item" ) )
                        {
                            int row = 0;
                            List<Element> rows = table.getAllElements ( HTMLElementName.TR );
                            for ( Element tr : rows )
                            {
                                if ( row == 0 )
                                {
                                    // Название
                                    item.setTitle (
                                            tr.getFirstElement ( HTMLElementName.B ).getContent ()
                                                    .getRenderer ().toString ().trim () );

                                    // Синоним
                                    Element synonymElement =
                                            tr.getFirstElement ( HTMLElementName.I );
                                    if ( synonymElement != null )
                                    {
                                        item.setSynonym (
                                                synonymElement.getContent ().getRenderer ()
                                                        .toString ().trim () );
                                    }
                                }
                                else
                                {
                                    // Строка с разными данными

                                    // Парсим заголовки (убираем пустые как ненужные)
                                    List<Element> headers =
                                            tr.getAllElements ( HTMLElementName.TH );
                                    List<Element> toDelete = new ArrayList<Element> ();
                                    for ( Element header : headers )
                                    {
                                        if ( header.getContent ().toString ().trim ()
                                                .equals ( "" ) )
                                        {
                                            toDelete.add ( header );
                                        }
                                    }
                                    headers.removeAll ( toDelete );

                                    // Парсим ячейки с данными под заголовками
                                    List<Element> datas = tr.getAllElements ( HTMLElementName.TD );
                                    for ( int i = 0; i < headers.size () && i < datas.size (); i++ )
                                    {
                                        Element header = headers.get ( i );
                                        Element data = datas.get ( i );
                                        parseCell ( item, header, data );
                                    }
                                }
                                row++;
                            }
                            break;
                        }
                    }

                    if ( shouldStop )
                    {
                        shouldStop = false;
                        break;
                    }

                    foundItemsList.add ( item );
                    foundItems.put ( item.getId (), item );
                }
                else
                {
                    System.out.println ( "Item #" + id + " doesn't exist" );
                }
            }
            else
            {
                System.out.println (
                        "Unable to load page: " + RagUtils.getRatemyserverLink ( "" + id ) +
                                ( retryCount > 0 ? " #" + retryCount : "" ) );
                retryCount++;
                continue;
            }

            timeSpent += System.currentTimeMillis () - time;
            count++;

            currentId = id + 1;
            try
            {
                SwingUtilities.invokeAndWait ( new Runnable ()
                {
                    public void run ()
                    {
                        listener.actionPerformed ( null );
                    }
                } );
            }
            catch ( InterruptedException e )
            {
                e.printStackTrace ();
            }
            catch ( InvocationTargetException e )
            {
                e.printStackTrace ();
            }

            if ( shouldStop )
            {
                shouldStop = false;
                break;
            }

            retryCount = 0;
            id++;
        }

        if ( currentId > endId )
        {
            saveResult ();
        }
    }

    private static void saveResult ()
    {
        System.out.println ( "Adding " + foundItems.size () + " into old data map..." );
        ValuesTable<String, Item> items = RagManager.getKnownItems ();
        items.addAll ( foundItems );

        System.out.println ( "Saving " + items.size () + " items into data file..." );
        XmlUtils.toXML ( items.getValues (), new File ( "src/com/rag/data/data.xml" ) );

        System.out.println ( "Finished." );
    }

    private static String getHumanTime ( long time )
    {
        long seconds = time / 1000;
        if ( seconds < 60 )
        {
            return seconds + " сек";
        }
        else if ( seconds < 60 * 60 )
        {
            return seconds / 60 + " м " + seconds % 60 + " с";
        }
        else
        {
            return seconds / 3600 + "ч " + ( seconds % 3600 ) / 60 + " м " + seconds % 60 + " с";
        }
    }

    private static void parseCell ( Item item, Element header, Element data )
    {
        String propertyName = header.getContent ().toString ().trim ();
        String propertyValue = data.getContent ().toString ().trim ();
        if ( propertyName.equals ( "Type" ) )
        {
            item.setItemType ( ItemType.getItemType ( propertyValue ) );
        }
        else if ( propertyName.equals ( "Class" ) )
        {
            item.setItemClass ( ItemClass.getItemClass ( propertyValue ) );
        }
        else if ( propertyName.equals ( "Buy" ) )
        {
            try
            {
                item.setBuyPrice ( Integer.parseInt (
                        propertyValue.substring ( 0, propertyValue.length () - 2 ) ) );
            }
            catch ( Throwable e )
            {
                item.setBuyPrice ( null );
            }
        }
        else if ( propertyName.equals ( "Sell" ) )
        {
            try
            {
                item.setSellPrice ( Integer.parseInt (
                        propertyValue.substring ( 0, propertyValue.length () - 2 ) ) );
            }
            catch ( Throwable e )
            {
                item.setSellPrice ( null );
            }
        }
        else if ( propertyName.equals ( "Weight" ) )
        {
            item.setWeight ( Float.parseFloat ( propertyValue ) );
        }
        else if ( propertyName.equals ( "Pre/Suffix" ) )
        {
            item.setCardSuffix ( propertyValue );
        }
        else if ( propertyName.equals ( "Attack" ) )
        {
            try
            {
                item.setAttack ( Integer.parseInt ( propertyValue ) );
            }
            catch ( Throwable e )
            {
                item.setAttack ( Integer.parseInt (
                        propertyValue.substring ( 0, propertyValue.indexOf ( " / " ) ) ) );
            }
        }
        else if ( propertyName.equals ( "Defense" ) )
        {
            item.setDefense ( Integer.parseInt ( propertyValue ) );
        }
        else if ( propertyName.equals ( "Required Lvl" ) )
        {
            try
            {
                item.setRequiredLvl ( Integer.parseInt ( propertyValue ) );
            }
            catch ( Throwable e )
            {
                item.setRequiredLvl ( null );
            }
        }
        else if ( propertyName.equals ( "Weapon Lvl" ) )
        {
            if ( propertyValue.contains ( "n/a" ) )
            {
                item.setWeaponLvl ( null );
            }
            else
            {
                item.setWeaponLvl ( Integer.parseInt ( propertyValue ) );
            }
        }
        else if ( propertyName.equals ( "Slot" ) )
        {
            item.setSlotCount ( Integer.parseInt ( propertyValue ) );
        }
        else if ( propertyName.equals ( "Range" ) )
        {
            item.setRange ( Integer.parseInt ( propertyValue ) );
        }
        else if ( propertyName.equals ( "Description" ) )
        {
            if ( propertyValue.equals ( "Not Available" ) )
            {
                item.setDescription ( null );
            }
            else
            {
                item.setDescription ( propertyValue );
            }
        }
        else if ( propertyName.equals ( "Item Script" ) )
        {
            item.setScript ( data.getContent ().getTextExtractor ().toString () );
        }
        else if ( propertyName.equals ( "Dropped By" ) )
        {
            if ( propertyValue.contains ( "No Result" ) )
            {
                item.setDropRates ( null );
            }
            else
            {
                Segment content = data.getContent ();
                List<MobDrop> dropRates = new ArrayList<MobDrop> ();
                for ( Element link : content.getAllElements ( HTMLElementName.A ) )
                {
                    String href = link.getAttributeValue ( "href" );
                    String text = link.getTextExtractor ().toString ();
                    int dropStart = text.lastIndexOf ( "(" );
                    int dropEnd = text.lastIndexOf ( "%)" );

                    MobDrop mobDrop = new MobDrop ();
                    mobDrop.setId ( href.substring ( href.lastIndexOf ( "=" ) + 1 ) );
                    mobDrop.setName ( text.substring ( 0, dropStart ).trim () );
                    mobDrop.setItemId ( item.getId () );
                    mobDrop.setDropRate (
                            Float.parseFloat ( text.substring ( dropStart + 1, dropEnd ) ) );
                    mobDrop.setMvpDrop ( text.contains ( "[mvp drop]" ) ? true : null );
                    dropRates.add ( mobDrop );
                }
                item.setDropRates ( dropRates );
            }
        }
    }

    public static String loadInfoPage ( String id )
    {
        try
        {
            return FileUtils.readToString ( new URL ( RagUtils.getRatemyserverLink ( id ) ) );
        }
        catch ( Throwable ex )
        {
            System.out.println ( ex.getMessage () );
        }
        return null;
    }
}